package com.gt.conf.security;

import com.gt.filter.AuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @version V1.0.0
 * @Description Spring Security 的配置类
 * @Author liuyuequn weanyq@gmail.com
 * @Date 2017/10/3 0:02
 */
@Configuration      // 声明为配置类
@EnableWebSecurity      // 启用 Spring Security web 安全的功能
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 注册 401 处理器
     */
    @Autowired
    private EntryPointUnauthorizedHandler unauthorizedHandler;

    /**
     * 注册 403 处理器
     */
    @Autowired
    private MyAccessDeniedHandler accessDeniedHandler;

    /**
     * 注册 token 转换拦截器为 bean
     * 如果客户端传来了 token ，那么通过拦截器解析 token 赋予用户权限
     *
     * @return
     * @throws Exception
     */
    @Bean
    public AuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
        AuthenticationTokenFilter authenticationTokenFilter = new AuthenticationTokenFilter();
        authenticationTokenFilter.setAuthenticationManager(authenticationManagerBean());
        return authenticationTokenFilter;
    }

    /**
     * 表达式
     描述
     hasRole([role])
     当前用户是否拥有指定角色。
     hasAnyRole([role1,role2])
     多个角色是一个以逗号进行分隔的字符串。如果当前用户拥有指定角色中的任意一个则返回true。
     hasAuthority([auth])
     等同于hasRole
     hasAnyAuthority([auth1,auth2])
     等同于hasAnyRole
     Principle
     代表当前用户的principle对象
     authentication
     直接从SecurityContext获取的当前Authentication对象
     permitAll
     总是返回true，表示允许所有的
     denyAll
     总是返回false，表示拒绝所有的
     isAnonymous()
     当前用户是否是一个匿名用户
     isRememberMe()
     表示当前用户是否是通过Remember-Me自动登录的
     isAuthenticated()
     表示当前用户是否已经登录认证成功了。
     isFullyAuthenticated()
     如果当前用户既不是一个匿名用户，同时又不是通过Remember-Me自动登录的，则返回true。
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/auth").authenticated()       // 需携带有效 token
                .antMatchers("/admin").hasAuthority("admin")   // 需拥有 admin 这个权限
                .antMatchers("/ADMIN").hasRole("ADMIN")     // 需拥有 ADMIN 这个身份
                .antMatchers("/getU/**").hasAuthority("admin")
                .antMatchers("/t/add").hasAnyAuthority("why","admin")
//                .antMatchers("/receiveDebug").permitAll()//接收bug不拦截
//                .antMatchers("/sys/**").permitAll()//页面不拦截
//                .antMatchers("/css/**","/assets/**","/js/**","/images/**").permitAll()//静态资源不拦截
                .antMatchers("/sys/index").hasAnyAuthority("user")
//                .anyRequest().authenticated()     //所有请求都需要验证
                .anyRequest().permitAll()     //所有请求都不拦截
                .and()
                // 配置被拦截时的处理
                .exceptionHandling()
                .authenticationEntryPoint(this.unauthorizedHandler)   // 添加 token 无效或者没有携带 token 时的处理
                .accessDeniedHandler(this.accessDeniedHandler)      //添加无权限时的处理
                .and()
                .csrf()
                .disable()                      // 禁用 Spring Security 自带的跨域处理
                .sessionManagement()                        // 定制我们自己的 session 策略
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 调整为让 Spring Security 不创建和使用 session


        /**
         * 本次 json web token 权限控制的核心配置部分
         * 在 Spring Security 开始判断本次会话是否有权限时的前一瞬间
         * 通过添加过滤器将 token 解析，将用户所有的权限写入本次会话
         */
        http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
    }
}
